 `timescale 1ns/1ps
`include "../define.svh"

//------------------------------------------------------------------
// 选择测试类型（取消注释其中一个）
//------------------------------------------------------------------
`define TEST_FP16       // 测试FP16
// `define TEST_FP32    // 测试FP32
//`define TEST_MIXED     // 混合精度模式（FP16输入，FP32输出）
module mult_top_tb;

//------------------------------------------------------------------
// 参数定义（根据宏自动配置）
//------------------------------------------------------------------
`ifdef TEST_FP16
    localparam string TEST_FILE = "D:/FPGA/TPU/TPU/fp16_mult_cases.txt";
    localparam DATA_WIDTH = 16;    // FP16位宽
    localparam MASK       = 32'h0000FFFF; // 校验掩码
    localparam DATA_TYPE = `FP16;
    localparam MIX_EN    = 1'b0;
`elsif TEST_FP32
    localparam string TEST_FILE = "D:/FPGA/TPU/TPU/fp32_mult_cases.txt";
    localparam DATA_WIDTH = 32;    // FP32位宽
    localparam MASK       = 32'hFFFFFFFF; // 校验掩码
    localparam DATA_TYPE = `FP32;
    localparam MIX_EN    = 1'b0;
`endif
`ifdef TEST_MIXED
    localparam string TEST_FILE = "D:/FPGA/TPU/TPU/fp16_to_fp32_mult_cases.txt";
    localparam INPUT_WIDTH  = 16;    // 输入位宽（FP16）
    localparam OUTPUT_WIDTH = 32;    // 输出位宽（FP32）
    localparam MASK         = 32'hFFFFFFFF;  // 全32位校验
    localparam DATA_TYPE    = `FP16;         // 输入类型标识
    localparam MIX_EN       = 1'b1;
`endif
//------------------------------------------------------------------
// 信号定义（统一为32位接口）
//------------------------------------------------------------------
reg clk;
reg sys_rst_n;
reg en;
reg [31:0] data_in;      // 输入数据（低16/32位有效）
reg [31:0] weight_in;     // 输入权重
wire [31:0] float_o;      // 输出结果
wire mult_out_valid;

// 测试项结构体
typedef struct {
    logic [31:0] a;
    logic [31:0] b;
    logic [31:0] expected;
    string       test_name;
} test_item;
test_item test_queue[$];
int test_case_num = 0;

//------------------------------------------------------------------
// 时钟生成（200MHz）
//------------------------------------------------------------------
always #2.5 clk = ~clk;

//------------------------------------------------------------------
// 实例化被测模块
//------------------------------------------------------------------
Mult_top uut (
    .clk(clk),
    .sys_rst_n(sys_rst_n),
    .en(en),
    .data_type(DATA_TYPE),       // 根据设计需求调整
    .mix_precision(MIX_EN),
    .data_in(data_in),
    .weight_in(weight_in),
    .float_o(float_o),
    .mult_out_valid(mult_out_valid)
);

//------------------------------------------------------------------
// 文件读取与测试队列初始化
//------------------------------------------------------------------
initial begin
    int fd;
    logic [31:0] a, b, expected;
    string line;
    
    // 打开测试文件
    fd = $fopen(TEST_FILE, "r");
    if (!fd) begin
        $display("Error: Failed to open test file!");
        $finish;
    end
    
    // 读取测试用例
    while (!$feof(fd)) begin
        void'($fgets(line, fd));
        if (line.len() == 0 || line.substr(0,1) == "//") continue;
        
        if ($sscanf(line, "%h %h %h", a, b, expected) == 3) begin
            test_queue.push_back('{
                a: a,
                b: b,
                expected: expected,
                test_name: $sformatf("Case%0d: A=%h B=%h", test_case_num+1, a, b)
            });
            test_case_num++;
        end
        if (test_case_num == 100) break;     
    end
    $fclose(fd);
end
//------------------------------------------------------------------
// 主测试流程（修改后）
//------------------------------------------------------------------
initial begin
    // 初始化信号
    clk = 0;
    sys_rst_n = 0;
    en = 0;
    data_in = 0;
    weight_in = 0;

    // 复位
    repeat(2) @(posedge clk);
    sys_rst_n = 1;
    @(posedge clk);
    en = 1;  // 使能信号拉高

    // 驱动测试用例
    fork
// 修改后的输入驱动线程
begin
    //@(posedge clk); // 等待一个周期
    data_in   = test_queue[0].a;
    weight_in = test_queue[0].b;
    $display("[%t] Sent: %s", $time, test_queue[0].test_name);
    
    // 后续数据每个周期更新
    for (int i=1; i<test_case_num; i++) begin
        @(posedge clk);
        data_in   = test_queue[i].a;
        weight_in = test_queue[i].b;
        $display("[%t] Sent: %s", $time, test_queue[i].test_name);
    end
end

        // 结果校验线程（保持原逻辑）
        begin
            automatic int result_index = 0;
            forever begin
                @(posedge clk);
                if (mult_out_valid) begin
                    if (result_index < test_case_num) begin
                        check_result(
                            test_queue[result_index].expected,
                            test_queue[result_index].test_name
                        );
                        result_index++;
                    end
                end
            end
        end
    join

    #100;
    $display("All %0d test cases completed!", test_case_num);
    $finish;
end
//------------------------------------------------------------------
// 结果校验任务
//------------------------------------------------------------------
task check_result;
    input [31:0] expected;
    input string test_name;
    begin
        automatic logic [31:0] actual = float_o & MASK;
        automatic logic is_nan_expected = (expected[30:23] == 8'hFF && expected[22:0] != 0);
        automatic logic is_nan_actual = (actual[30:23] == 8'hFF && actual[22:0] != 0);

        if (is_nan_expected) begin
            if (!is_nan_actual) begin
                $error("[%t] FAIL (NaN Expected): %s", $time, test_name);
                $display("  Actual: %h (FP32: %e)", actual, $bitstoreal(actual));
            end else begin
                $display("[%t] PASS (NaN): %s", $time, test_name);
            end
        end else if (actual !== expected) begin
            $error("[%t] FAIL: %s", $time, test_name);
            $display("  Expected: %h (FP32: %e)", expected, $bitstoreal(expected));
            $display("  Actual:   %h (FP32: %e)", actual, $bitstoreal(actual));
        end else begin
            $display("[%t] PASS: %s", $time, test_name);
        end
    end
endtask

property output_latency_check;
    @(posedge clk) 
    (en && $past(en, 2)) |-> mult_out_valid;
endproperty
assert property(output_latency_check) else $error("Latency violation!");

endmodule 